<?php

/**
 * CMail class file.
 *
 * @author Jonah Turnquist <poppitypop@gmail.com>
 * @link https://code.google.com/p/yii-mail/
 * @package Yii-Mail
 */

/**
 * YiiMail is an application component used for sending email.
 *
 * You may configure it as below.  Check the public attributes and setter
 * methods of this class for more options.
 * <pre>
 * return array(
 * 	...
 * 	'import => array(
 * 		...
 * 		'ext.mail.YiiMailMessage',
 * 	),
 * 	'components' => array(
 * 		'mail' => array(
 * 			'class' => 'ext.yii-mail.YiiMail',
 * 			'transportType' => 'php',
 * 			'viewPath' => 'application.views.mail',
 * 			'logging' => true,
 * 			'dryRun' => false
 * 		),
 * 		...
 * 	)
 * );
 * </pre>
 *
 * Example usage:
 * <pre>
 * $message = new YiiMailMessage;
 * $message->setBody('Message content here with HTML', 'text/html');
 * $message->subject = 'My Subject';
 * $message->addTo('johnDoe@domain.com');
 * $message->from = Yii::app()->params['adminEmail'];
 * Yii::app()->mail->send($message);
 * </pre>
 */
class CMail extends CApplicationComponent {

	/**
	 * @var bool whether to log messages using Yii::log().
	 * Defaults to true.
	 */
	public $logging = true;

	/**
	 * @var bool whether to disable actually sending mail.
	 * Defaults to false.
	 */
	public $dryRun = false;

	/**
	 * @var string the delivery type.  Can be either 'php' or 'smtp'.  When
	 * using 'php', PHP's {@link mail()} function will be used.
	 * Defaults to 'php'.
	 */
	public $transportType = 'php';

	/**
	 * @var string the path to the location where mail views are stored.
	 * Defaults to 'application.views.mail'.
	 */
	public $viewPath = 'application.views.mail';

	/**
	 * @var string options specific to the transport type being used.
	 * To set options for STMP, set this attribute to an array where the keys
	 * are the option names and the values are their values.
	 * Possible options for SMTP are:
	 * <ul>
	 * 	<li>host</li>
	 * 	<li>username</li>
	 * 	<li>password</li>
	 * 	<li>port</li>
	 * 	<li>encryption</li>
	 * 	<li>timeout</li>
	 * 	<li>extensionHandlers</li>
	 * </ul>
	 * See the SwiftMailer documentaion for the option meanings.
	 */
	public $transportOptions;

	/**
	 * @var mixed Holds the SwiftMailer transport
	 */
	protected $transport;

	/**
	 * @var mixed Holds the SwiftMailer mailer
	 */
	protected $mailer;
	private static $registeredScripts = false;

	/**
	 * Calls the {@link registerScripts()} method.
	 */
	public function init() {
		$this->registerScripts();
		parent::init();
	}

	/**
	 * Send a {@link YiiMailMessage} as it would be sent in a mail client.
	 *
	 * All recipients (with the exception of Bcc) will be able to see the other
	 * recipients this message was sent to.
	 *
	 * If you need to send to each recipient without disclosing details about the
	 * other recipients see {@link batchSend()}.
	 *
	 * Recipient/sender data will be retreived from the {@link YiiMailMessage}
	 * object.
	 *
	 * The return value is the number of recipients who were accepted for
	 * delivery.
	 *
	 * @param YiiMailMessage $message
	 * @param array &$failedRecipients, optional
	 * @return int
	 * @see batchSend()
	 */
	public function send(MailMessage $message, &$failedRecipients = null) {
		if ($this->logging === true) self::log($message);
		if ($this->dryRun === true) return count($message->to);
		else return $this->getMailer()->send($message->message, $failedRecipients);
	}

	/**
	 * Send the given {@link YiiMailMessage} to all recipients individually.
	 *
	 * This differs from {@link send()} in the way headers are presented to the
	 * recipient.  The only recipient in the "To:" field will be the individual
	 * recipient it was sent to.
	 *
	 * If an iterator is provided, recipients will be read from the iterator
	 * one-by-one, otherwise recipient data will be retreived from the
	 * {@link YiiMailMessage} object.
	 *
	 * Sender information is always read from the {@link YiiMailMessage} object.
	 *
	 * The return value is the number of recipients who were accepted for
	 * delivery.
	 *
	 * @param YiiMailMessage $message
	 * @param array &$failedRecipients, optional
	 * @param Swift_Mailer_RecipientIterator $it, optional
	 * @return int
	 * @see send()
	 */
	public function batchSend(MailMessage $message, &$failedRecipients = null,
		Swift_Mailer_RecipientIterator $it = null) {
		if ($this->logging === true) self::log($message);
		if ($this->dryRun === true) return count($message->to);
		else
				return $this->getMailer()->batchSend($message->message, $failedRecipients,
					$it);
	}

	/**
	 * Sends a message in an extremly simple but less extensive way.
	 *
	 * @param mixed from address, string or array of the form $address => $name
	 * @param mixed to address, string or array of the form $address => $name
	 * @param string subject
	 * @param string body
	 */
	public function sendSimple($from, $to, $subject, $body) {
		$message = new MailMessage;
		$message->setSubject($subject)
			->setFrom($from)
			->setTo($to)
			->setBody($body, 'text/html');

		if ($this->logging === true) self::log($message);
		if ($this->dryRun === true) return count($message->to);
		else return $this->getMailer()->send($message);
	}

	/**
	 * Logs a YiiMailMessage in a (hopefully) readable way using Yii::log.
	 * @return string log message
	 */
	public static function log(MailMessage $message) {
		$msg = 'Sending email to ' . implode(', ', array_keys($message->to)) . "\n" .
			implode('', $message->headers->getAll()) . "\n" .
			$message->body
		;
		Yii::log($msg, CLogger::LEVEL_INFO, 'ext.yii-mail.YiiMail'); // TODO: attempt to determine alias/category at runtime
		return $msg;
	}

	/**
	 * Gets the SwiftMailer transport class instance, initializing it if it has
	 * not been created yet
	 * @return mixed {@link Swift_MailTransport} or {@link Swift_SmtpTransport}
	 */
	public function getTransport() {
		if ($this->transport === null) {
			switch ($this->transportType) {
				case 'php':
					$this->transport = Swift_MailTransport::newInstance();
					if ($this->transportOptions !== null)
							$this->transport->setExtraParams($this->transportOptions);
					break;
				case 'smtp':
					$this->transport = Swift_SmtpTransport::newInstance();
					foreach ($this->transportOptions as $option=>$value)
							$this->transport->{'set' . ucfirst($option)}($value); // sets option with the setter method
					break;
			}
		}

		return $this->transport;
	}

	/**
	 * Gets the SwiftMailer {@link Swift_Mailer} class instance
	 * @return Swift_Mailer
	 */
	public function getMailer() {
		if ($this->mailer === null)
				$this->mailer = Swift_Mailer::newInstance($this->getTransport());

		return $this->mailer;
	}

	/**
	 * Registers swiftMailer autoloader and includes the required files
	 */
	public function registerScripts() {
		if (self::$registeredScripts) return;
		self::$registeredScripts = true;
		require dirname(__FILE__) . '/CMail/classes/Swift.php';
		Yii::registerAutoloader(array('Swift', 'autoload'));
		require dirname(__FILE__) . '/CMail/swift_init.php';
	}

}